package org.urchr.beorganized.dao;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.urchr.beorganized.api.base.Log;
import org.urchr.beorganized.dao.errors.*;
import org.urchr.beorganized.dao.tables.Note;
import org.urchr.beorganized.dao.tables.Permission;
import org.urchr.beorganized.dao.tables.Session;
import org.urchr.beorganized.dao.tables.User;
import org.urchr.web.db.DBSession;
import org.w3c.dom.Element;

public class UsersDAO {
	
	public static void updateUser(int userID, User data) throws DBErrorException, NameTooShortException{
		DBSession session = new DBSession();
		try {
			session.begin();
			User user = getCurrent(session, userID);
			if(user!=null){
				if(data.getName()==null || data.getName().length()<5)
					throw new NameTooShortException();
				user.setName(data.getName());
				user.setDateFormat(data.getDateFormat());
				user.setEmail(data.getEmail());
//				user.setMinuteOffset(data.getMinuteOffset());
				user.setTimeFormat(data.getTimeFormat());
				user.setTimeZone(data.getTimeZone());
				session.save(user);
			}
			session.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.rollback();
			throw new DBErrorException();
		} finally {
			session.close();
		}
	}
	
	public static void updatePassword(int userID, String oldPassword, String password, boolean md5) throws DBErrorException, InvalidUserPassException {
		DBSession session = new DBSession();
		try {
			session.begin();
			User user = getCurrent(session, userID);
			if(user!=null){
				if(!user.getPassword().equals(md5?oldPassword:MD5.makeHash(oldPassword))){
					session.rollback();
					throw new InvalidUserPassException();
				}
				user.setPassword(md5?password:MD5.makeHash(password));
				session.save(user);
			}
			session.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.rollback();
			throw new DBErrorException();
		} finally {
			session.close();
		}
	}

	
	public static User registerUser(Element u) throws UserExistsException, 
			LoginTooShortException, InvalidLoginException, 
			NameTooShortException, PasswordTooShortException, DBErrorException{
		DBSession session = new DBSession();
		if(u.getAttribute("login")==null || u.getAttribute("login").length()<5)
			throw new LoginTooShortException();
		if(!u.getAttribute("login").matches("\\w+"))
			throw new InvalidLoginException();
		if(u.getAttribute("name")==null || u.getAttribute("name").length()<5)
			throw new NameTooShortException();
		if(u.getAttribute("password")==null || u.getAttribute("password").length()<5)
			throw new PasswordTooShortException();
		
		try {
			List<User> users = session.createQuery("from User where login=:login").
					setString("login", u.getAttribute("login")).list();
			if(users.size()>0)
				throw new UserExistsException();
			
			session.begin();
			User user = new User();
			user.setCreateDate(new Date());
			user.setLogin(u.getAttribute("login"));
			user.setName(u.getAttribute("name"));
			user.setEmail(u.getAttribute("email"));
			user.setPassword(MD5.makeHash(u.getAttribute("password")));
//			user.setMinuteOffset(NotesDAO.safeInt(u.getAttribute("minuteOffset"), 0));
			user.setTimeZone(u.getAttribute("timeZone"));
			session.save(user);
			
			Note note = new Note();
			note.setCreateDate(new Date());
			note.setEditDate(new Date());
			note.setName(u.getAttribute("name"));
			note.setLevel(0);
			note.setLeft(0);
			note.setRight(1);
			note.setUser(user);
			note.setEditUser(user);
			note.setShareCode(Note.genRandomString());
			session.save(note);
			
			Permission permission = new Permission();
			permission.setNote(note);
			permission.setUser(user);
			
			permission.setAllowCreate(1);
			permission.setAllowEdit(1);
			permission.setAllowDelete(1);
			session.save(permission);
			
			session.commit();
			return user;
		} catch (HibernateException e) {
			e.printStackTrace();
			session.rollback();
			throw new DBErrorException();
		}finally{
			session.close();
		}
	}
	
	public static User updateUserTimeZone(User user){
		if(null==user.getTimeZone() || "".equals(user.getTimeZone())){
			Map<Integer, String> tz = new HashMap<Integer, String>();
			tz.put(-720, "Etc/GMT+12");
			tz.put(-660, "US/Samoa");
			tz.put(-600, "US/Hawaii");
			tz.put(-570, "Pacific/Marquesas");
			tz.put(-540, "US/Alaska");
			tz.put(-480, "PST");
			tz.put(-420, "MST");
			tz.put(-360, "CST");
			tz.put(-300, "EST");
			tz.put(-270, "America/Caracas");
			tz.put(-240, "PRT");
			tz.put(-210, "CNT");
			tz.put(-180, "BET");
			tz.put(-120, "Atlantic/South_Georgia");
			tz.put(-60, "Atlantic/Cape_Verde");
			tz.put(0, "GMT");
			tz.put(60, "CET");
			tz.put(120, "EET");
			tz.put(180, "Europe/Moscow");
			tz.put(210, "Asia/Tehran");
			tz.put(240, "Europe/Samara");
			tz.put(300, "Asia/Yekaterinburg");
			tz.put(330, "Asia/Colombo");
			tz.put(345, "Asia/Katmandu");
			tz.put(360, "Asia/Novosibirsk");
			tz.put(390, "Asia/Rangoon");
			tz.put(420, "Asia/Krasnoyarsk");
			tz.put(480, "Asia/Irkutsk");
			tz.put(525, "Australia/Eucla");
			tz.put(540, "Asia/Tokyo");
			tz.put(570, "ACT");
			tz.put(600, "AET");
			tz.put(630, "Australia/LHI");
			tz.put(660, "SST");
			tz.put(690, "Pacific/Norfolk");
			tz.put(720, "Asia/Anadyr");
			tz.put(765, "Pacific/Chatham");
			tz.put(780, "Pacific/Enderbury");
			tz.put(840, "Pacific/Kiritimati");
			DBSession session = new DBSession();
			try {
				session.begin();
				User u = (User) session.get(User.class, user.getId());
				Log.debug(null, "Updating tz for user: "+user.getLogin()+": "+tz.get(user.getMinuteOffset()));
				u.setTimeZone(tz.get(user.getMinuteOffset()));
				session.save(u);
				session.commit();
				return u;
			} catch (HibernateException e) {
				session.rollback();
			}finally{
				session.close();
			}
		}else{
//			Log.debug(null, "Users timezone already updated: "+user.getTimeZone());
		}
		return user;
	}
	
	public static void userToNode(XMLDocumentBuilder doc, Element node, User user){
		node.setAttribute("login", user.getLogin());
		node.setAttribute("name", user.getName());
		node.setAttribute("email", user.getEmail());
		node.setAttribute("dateFormat", user.getDateFormat());
		node.setAttribute("timeFormat", user.getTimeFormat());
		node.setAttribute("timeZone", user.getTimeZone());
		node.setAttribute("minuteOffset", ""+user.getMinuteOffset());
		node.setAttribute("language", user.getLanguage());
		node.setAttribute("id", ""+user.getId());
		node.setAttribute("rootNoteID", ""+user.getRootNoteID());
	}
	
	public static User authorizeUser(String userName, String password, boolean md5)
		throws InvalidUserPassException {
		DBSession session = new DBSession();
		try {
			if(userName!=null && password!=null){
				List<User> res = session.createQuery("from User " +
						"where login=:login and password=:pass").
						setString("login", userName).
						setString("pass", md5?password:MD5.makeHash(password)).list();
				if(res.size()==1){
					User user = res.get(0);
					List<Note> rootNotes = session.createQuery("from Note n " +
							"where n.user=:user and n.level=:level").
						setEntity("user", user).
						setInteger("level", 0).
					list();
					if(rootNotes.size()>0)
						user.setRootNoteID(rootNotes.get(0).getId());
					return user;
				}
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			session.close();
		}
		throw new InvalidUserPassException();
	}
	
	public static User getUser(int userID) throws DBErrorException{
		DBSession session = new DBSession();
		try {
			User user = getCurrent(session, userID);
			if(user!=null){
				List<Note> rootNotes = session.createQuery("from Note n " +
						"where n.user=:user and n.level=:level").
					setEntity("user", user).
					setInteger("level", 0).
				list();
				if(rootNotes.size()>0)
					user.setRootNoteID(rootNotes.get(0).getId());
				return user;
			}
		} catch (HibernateException e) {
			// TODO: handle exception
		} finally {
			session.close();
		}
		throw new DBErrorException();
	}
	
	public static User getCurrent(DBSession session, int userID){
		try {
			User user = (User) session.get(User.class, userID);
			if(user!=null)
				return user;
		} catch (HibernateException e) {
			e.printStackTrace();
			// TODO: handle exception
		}
		return null;
	}
	
	public static String createSession(int userID) throws DBErrorException{
		DBSession session = new DBSession();
		try {
			User user = getCurrent(session, userID);
			session.begin();
			Session sess = new Session();
			sess.setCode(Note.genRandomString());
			sess.setUser(user);
			Calendar cal = Calendar.getInstance();
			cal.add(cal.DAY_OF_YEAR, 14);
			sess.setValid(cal.getTime());
			session.save(sess);
			session.commit();
			return sess.getCode();
		} catch (HibernateException e) {
			session.rollback();
		} finally {
			session.close();
		}
		throw new DBErrorException();		
	}
	
	public static int checkSession(String code){
		DBSession session = new DBSession();
		try {
			List<User> users = session.createQuery("select user from Session s where s.code=:code").
					setString("code", code).list();
			if(users.size()==1)
				return users.get(0).getId();
		} catch (HibernateException e) {
			session.rollback();
		} finally {
			session.close();
		}
		return -1;
	}
	
	public static void clearUserSession(int userID, String code){
		DBSession session = new DBSession();
		try {
			session.begin();
			List<Session> list = session.createQuery("from Session where valid<:now").
					setDate("now", new Date()).
				list();
			for (Session s : list) {
				session.getSession().delete(s);	
			}
			User user = getCurrent(session, userID);
			if(user!=null){
				list = session.createQuery("from Session where code=:code and user=:user").
						setString("code", code).
						setEntity("user", user).
					list();
				for (Session s : list) {
					session.getSession().delete(s);
				}
			}
			session.commit();
		} catch (HibernateException e) {
			session.rollback();
		} finally {
			session.close();
		}
		
	}
}
